"""
Input sanitization utilities for user-generated content
"""
import re
import html
from typing import Optional, Dict, Any, List
import logging

logger = logging.getLogger(__name__)

class InputSanitizer:
    """Sanitize user input to prevent security issues"""
    
    # Patterns for dangerous content
    SCRIPT_PATTERN = re.compile(r'<script[^>]*>.*?</script>', re.IGNORECASE | re.DOTALL)
    HTML_TAG_PATTERN = re.compile(r'<[^>]+>')
    SQL_INJECTION_PATTERN = re.compile(r'\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|CREATE|ALTER|EXEC|EXECUTE)\b', re.IGNORECASE)
    XSS_PATTERNS = [
        re.compile(r'javascript:', re.IGNORECASE),
        re.compile(r'on\w+\s*=', re.IGNORECASE),  # onclick, onload, etc.
        re.compile(r'<iframe', re.IGNORECASE),
        re.compile(r'<embed', re.IGNORECASE),
        re.compile(r'<object', re.IGNORECASE),
    ]
    
    # Maximum lengths
    MAX_TITLE_LENGTH = 200
    MAX_EXCERPT_LENGTH = 1000
    MAX_COMMENT_LENGTH = 5000
    MAX_CONTENT_LENGTH = 50000
    
    @classmethod
    def sanitize_text(cls, text: str, max_length: Optional[int] = None) -> str:
        """
        Sanitize plain text input
        
        Args:
            text: Input text to sanitize
            max_length: Maximum allowed length
            
        Returns:
            Sanitized text
        """
        if not text:
            return ""
        
        # HTML escape
        text = html.escape(text)
        
        # Remove script tags
        text = cls.SCRIPT_PATTERN.sub('', text)
        
        # Remove any remaining HTML tags
        text = cls.HTML_TAG_PATTERN.sub('', text)
        
        # Check for XSS patterns
        for pattern in cls.XSS_PATTERNS:
            if pattern.search(text):
                logger.warning(f"XSS pattern detected and removed: {pattern.pattern}")
                text = pattern.sub('', text)
        
        # Truncate if needed
        if max_length and len(text) > max_length:
            text = text[:max_length]
        
        # Remove null bytes
        text = text.replace('\x00', '')
        
        return text.strip()
    
    @classmethod
    def sanitize_title(cls, title: str) -> str:
        """Sanitize title text"""
        return cls.sanitize_text(title, cls.MAX_TITLE_LENGTH)
    
    @classmethod
    def sanitize_excerpt(cls, excerpt: str) -> str:
        """Sanitize excerpt text"""
        return cls.sanitize_text(excerpt, cls.MAX_EXCERPT_LENGTH)
    
    @classmethod
    def sanitize_comment(cls, comment: str) -> str:
        """Sanitize comment text"""
        return cls.sanitize_text(comment, cls.MAX_COMMENT_LENGTH)
    
    @classmethod
    def sanitize_content(cls, content: str) -> str:
        """Sanitize long content text"""
        return cls.sanitize_text(content, cls.MAX_CONTENT_LENGTH)
    
    @classmethod
    def sanitize_url(cls, url: str) -> Optional[str]:
        """
        Sanitize URL input
        
        Args:
            url: URL to sanitize
            
        Returns:
            Sanitized URL or None if invalid
        """
        if not url:
            return None
        
        # Basic URL validation
        url = url.strip()
        
        # Check for javascript: and data: URIs
        if url.lower().startswith(('javascript:', 'data:', 'vbscript:')):
            logger.warning(f"Dangerous URL protocol detected: {url[:50]}")
            return None
        
        # Ensure HTTP/HTTPS
        if not url.startswith(('http://', 'https://')):
            url = 'https://' + url
        
        # Remove any HTML/script injections
        if cls.HTML_TAG_PATTERN.search(url) or cls.SCRIPT_PATTERN.search(url):
            logger.warning(f"HTML/Script in URL detected: {url[:50]}")
            return None
        
        return url
    
    @classmethod
    def sanitize_dict(cls, data: Dict[str, Any], field_rules: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
        """
        Sanitize dictionary data
        
        Args:
            data: Dictionary to sanitize
            field_rules: Rules for specific fields (e.g., {'title': 'title', 'body': 'content'})
            
        Returns:
            Sanitized dictionary
        """
        if not data:
            return {}
        
        sanitized = {}
        field_rules = field_rules or {}
        
        for key, value in data.items():
            if key in field_rules:
                rule = field_rules[key]
                if rule == 'title':
                    sanitized[key] = cls.sanitize_title(value) if isinstance(value, str) else value
                elif rule == 'excerpt':
                    sanitized[key] = cls.sanitize_excerpt(value) if isinstance(value, str) else value
                elif rule == 'comment':
                    sanitized[key] = cls.sanitize_comment(value) if isinstance(value, str) else value
                elif rule == 'content':
                    sanitized[key] = cls.sanitize_content(value) if isinstance(value, str) else value
                elif rule == 'url':
                    sanitized[key] = cls.sanitize_url(value) if isinstance(value, str) else value
                else:
                    sanitized[key] = cls.sanitize_text(value) if isinstance(value, str) else value
            elif isinstance(value, str):
                # Default sanitization for strings
                sanitized[key] = cls.sanitize_text(value)
            elif isinstance(value, dict):
                # Recursive sanitization for nested dicts
                sanitized[key] = cls.sanitize_dict(value)
            elif isinstance(value, list):
                # Sanitize list items
                sanitized[key] = cls.sanitize_list(value)
            else:
                # Keep other types as-is
                sanitized[key] = value
        
        return sanitized
    
    @classmethod
    def sanitize_list(cls, items: List[Any]) -> List[Any]:
        """
        Sanitize list items
        
        Args:
            items: List to sanitize
            
        Returns:
            Sanitized list
        """
        sanitized = []
        for item in items:
            if isinstance(item, str):
                sanitized.append(cls.sanitize_text(item))
            elif isinstance(item, dict):
                sanitized.append(cls.sanitize_dict(item))
            elif isinstance(item, list):
                sanitized.append(cls.sanitize_list(item))
            else:
                sanitized.append(item)
        return sanitized
    
    @classmethod
    def check_sql_injection(cls, text: str) -> bool:
        """
        Check if text contains potential SQL injection
        
        Args:
            text: Text to check
            
        Returns:
            True if potential SQL injection detected
        """
        if not text:
            return False
        
        return bool(cls.SQL_INJECTION_PATTERN.search(text))
    
    @classmethod
    def sanitize_filename(cls, filename: str) -> str:
        """
        Sanitize filename to prevent path traversal
        
        Args:
            filename: Filename to sanitize
            
        Returns:
            Sanitized filename
        """
        if not filename:
            return "unnamed"
        
        # Remove path components
        filename = filename.replace('..', '')
        filename = filename.replace('/', '')
        filename = filename.replace('\\', '')
        
        # Remove special characters
        filename = re.sub(r'[^\w\s\-\.]', '', filename)
        
        # Limit length
        if len(filename) > 100:
            filename = filename[:100]
        
        return filename or "unnamed"